﻿using huqiang.Collections;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using UnityEngine;

namespace huqiang.Framework.Http
{
    public class HttpManager : HFBase
    {
        protected override string TypeName { get => "HttpManager"; }
        int _makTask = 8;
        bool _pause;
        /// <summary>
        /// 同时进行的最大任务数量
        /// </summary>
        public int MaxTask { get => _makTask; set {
                if (value < 1)
                    value = 1;
                else if (value > 16)
                    value = 16;
                _makTask = value;
            } }
        public bool Pause { get => _pause; set => _pause = value; }
        struct Mission : IClear
        {
            public object obj;
            public Action<object> action;
            public void Clear()
            {
                obj = null;
                action = null;
            }
        }
        struct Received
        {
            public HttpDownloader http;
            public long BytesReceived;
            public long TotalBytesToReceive;
        }
        LockStack<Mission> buffer = new LockStack<Mission>(256);
        void AddMainMission(Action<object> action, object obj)
        {
            Mission mission = new Mission();
            mission.action = action;
            mission.obj = obj;
            buffer.Push(mission);
        }

        LockList<HttpDownloader> ossMissions = new LockList<HttpDownloader>();
        public int MissionCount { get => ossMissions.Count; }
        public HttpDownloader Download(string saveFolder, string fileName, string uri, string blcok, string version, Action<IMission> done, int len = 0, bool filter = false)
        {
            if (filter)
                for (int i = 0; i < ossMissions.Count; i++)
                {
                    if (ossMissions[i].url == uri)//防止重复添加
                    {
                        return ossMissions[i];
                    }
                }
            if (!Directory.Exists(saveFolder))
                Directory.CreateDirectory(saveFolder);
            HttpDownloader mission = new HttpDownloader();
            mission.Name = fileName;
            mission.Block = blcok;
            mission.Version = version;
            mission.savePath = saveFolder + "/" + fileName;
            mission.url = uri;
            mission.MainThreadCallback = done;
            mission.Length = len;
            ossMissions.Add(mission);
            return mission;
        }
        public HttpDownloader FindByUrl(string uri)
        {
            for (int i = 0; i < ossMissions.Count; i++)
            {
                if (ossMissions[i].url == uri)
                    return ossMissions[i];
            }
            return null;
        }
        public HttpDownloader FindBySavePath(string path)
        {
            for (int i = 0; i < ossMissions.Count; i++)
            {
                if (ossMissions[i].savePath == path)
                    return ossMissions[i];
            }
            return null;
        }
        public void AddMission(HttpDownloader mission)
        {
            ossMissions.Add(mission);
        }
        public void AddToFirst(HttpDownloader mission)
        {
            ossMissions.Insert(0, mission);
        }
        public void MoveToFirst(HttpDownloader mission)
        {
            ossMissions.Remove(mission);
            ossMissions.Insert(0, mission);
        }
        public int GetDownloadBytes()
        {
            if (receiveds != null)
            {
                var http = receiveds[0].http;
                if (http != null)
                    return http.DownloadBytes;
            }
            return 0;
        }
        protected override void Update(float time)
        {
            Mission miss = new Mission();
            for (int i = 0; i < buffer.Count; i++)
            {
                if (buffer.Pop(ref miss))
                {
                    if (miss.action != null)
                    {
                        try
                        {
                            miss.action(miss.obj);
                        }
                        catch (Exception ex)
                        {
                            UnityEngine.Debug.LogError(ex.StackTrace);
                        }
                    }
                }
                else break;
            }
            if (!_pause)
                RunTask();
        }
        public void Clear()
        {
            ossMissions.Clear();
        }
        bool destroyed = false;
        public override void Dispose()
        {
            destroyed = true;
            ossMissions.Clear();
        }

        Thread[] threads;
        Received[] receiveds;
        
        void RunTask()
        {
            if (threads == null)
            {
                threads = new Thread[16];
                receiveds = new Received[16];
            }
            int max = ossMissions.Count;
            if (max > MaxTask)
                max = MaxTask;
            for (int i = 0; i < max; i++)
            {
                RunThread(i);
            }
        }
        void RunThread(int index)
        {
            if (threads[index] == null)
            {
                threads[index] = new Thread((o) => {
                    HWebClient web = new HWebClient();
                    while (true)
                    {
                        try
                        {
                            if (index > _makTask)
                                break;
                            if (_pause)
                                break;
                            var http = ossMissions.RemoveAt(0);
                            if (http == null)
                                break;
                            else
                            {
                                receiveds[index].http = http;
                                web.StartPos = 0;
                                if (http.savePath == "" || http.savePath == null)
                                {
                                    DownloadData(ref web, http);
                                }
                                else
                                {
                                    if (http.Length == 0)
                                    {
                                        if (GetLength(web, http) != WebExceptionStatus.Success)
                                        {
                                            goto label;
                                        }
                                    }
                                    DownloadFile(ref web, http);
                                }
                                label:;
                                if (http.Completed != null)
                                {
                                    http.Completed(http);
                                }
                                else if (http.MainThreadCallback != null)
                                {
                                    AddMainMission(
                                       (obj) =>
                                       {
                                           HttpDownloader hd = obj as HttpDownloader;
                                           hd.MainThreadCallback(hd);
                                       }, http);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Debug.LogError(ex.StackTrace);
                            break;
                        }
                        if (destroyed)
                            break;
                    }
                    threads[index] = null;
                    web.Dispose();
                });
                threads[index].Start(index);
            }
        }
        WebExceptionStatus GetLength(HWebClient web, HttpDownloader http)
        {
            label1:;
            try
            {
                http.Length = (int)web.GetFileLength(http.uri);
                http.code = (int)web.StatusCode;
            }
            catch (WebException wec)
            {
#if DEBUG
                UnityEngine.Debug.LogError(wec.Status);
                UnityEngine.Debug.LogError(http.url);
#endif
                if (wec.Status == WebExceptionStatus.ProtocolError)//url地址错误
                    return WebExceptionStatus.ProtocolError;
                if (destroyed)
                    return WebExceptionStatus.RequestCanceled;
                if (http.AutoRetry)
                    goto label1;
                return wec.Status;
            }
            return WebExceptionStatus.Success;
        }
        WebExceptionStatus DownloadData(ref HWebClient web, HttpDownloader http)
        {
            label1:;
            try
            {
                web.Done = false;
                http.data = web.DownloadData(http.uri);
                http.code = (int)web.StatusCode;
            }
            catch (WebException wec)
            {
#if DEBUG
                UnityEngine.Debug.LogError(wec.Status);
                UnityEngine.Debug.LogError(http.url);
#endif
                if (wec.Status == WebExceptionStatus.ProtocolError)//url地址错误
                    return WebExceptionStatus.ProtocolError;
                if (destroyed)
                    return WebExceptionStatus.RequestCanceled;
                web.Dispose();
                web = new HWebClient();
                if (http.AutoRetry)
                    goto label1;
                return wec.Status;
            }
            return WebExceptionStatus.Success;
        }
        WebExceptionStatus DownloadFile(ref HWebClient web, HttpDownloader http)
        {
            label1:;
            try
            {
                web.Done = false;
                web.DownloadFile(http.uri, http.savePath);
                http.code = (int)web.StatusCode;
            }
            catch (WebException wec)
            {
#if DEBUG
                UnityEngine.Debug.LogError(wec.Status);
                UnityEngine.Debug.LogError(http.url);
#endif
                if (wec.Status == WebExceptionStatus.ProtocolError)//url地址错误
                    return WebExceptionStatus.ProtocolError;
                if (destroyed)
                    return WebExceptionStatus.RequestCanceled;
                web.Dispose();
                web = new HWebClient();
                if (http.AutoRetry)
                    goto label1;
                return wec.Status;
            }
            return WebExceptionStatus.Success;
        }
        protected override void Initial()
        {
            ServicePointManager.DefaultConnectionLimit = 16;
        }
    }
}
